package tk.winshu.shortestpath.view;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tk.winshu.shortestpath.model.NodeData;
import tk.winshu.shortestpath.model.NodeStatus;
import tk.winshu.shortestpath.util.ExportUtil;
import tk.winshu.shortestpath.util.ImportUtil;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * @author winshu
 * @date 2015年2月3日
 */
public class MainFrame extends JFrame {

    private static final Logger log = LoggerFactory.getLogger(MainFrame.class.getName());
    private static final String ABOUT_DESCRIPTION = "作者 : winshu\n日期 : 2015.02.11\n更新 : 2018.08.16\n更新 : 2019.05.28\n更新 : 2019.10.19\n更新 : 2019.10.29\n更新 : 2020.11.08";

    /**
     * 画板
     */
    private CanvasPanel canvasPanel;

    /**
     * 是否有向图
     */
    private JCheckBox directedGraphCheckbox;
    /**
     * 显示权重
     */
    private JCheckBox showWeightCheckbox;
    /**
     * 循环运行
     */
    private JCheckBox keepLoopRunningCheckbox;

    /**
     * 运行/停止
     */
    private JButton runOrStopButton;
    /**
     * 调换起点/终点
     */
    private JButton exchangeButton;
    /**
     * 清除所有连线
     */
    private JButton cleanLinesButton;
    /**
     * 清除所有
     */
    private JButton cleanAllButton;

    /**
     * 提示
     */
    private JLabel tipLabel;

    public MainFrame() {
        setTitle("最短路径寻找算法");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setIconImage(NodeStatus.DEFAULT.getImage());

        createMenu();
        createContent();

        addListener();
        init();
    }

    private void addListener() {
        new DropTarget(canvasPanel, new DropTargetAdapter() {
            @Override
            public void drop(DropTargetDropEvent dropEvent) {
                onDrop(dropEvent);
            }
        });
        canvasPanel.setRunningCallback(() -> updateControl(false));
        showWeightCheckbox.addChangeListener(e -> canvasPanel.setWeightVisible(showWeightCheckbox.isSelected()));
        keepLoopRunningCheckbox.addChangeListener(e -> canvasPanel.setKeepLoopRunning(keepLoopRunningCheckbox.isSelected()));
        directedGraphCheckbox.addChangeListener(e -> canvasPanel.setDirectedGraph(directedGraphCheckbox.isSelected()));

        runOrStopButton.addActionListener(e -> runOrStop());
        exchangeButton.addActionListener(e -> canvasPanel.exchange());
        cleanLinesButton.addActionListener(e -> canvasPanel.cleanLines());
        cleanAllButton.addActionListener(e -> canvasPanel.cleanAll());

        this.setFocusable(true);
    }

    private void createContent() {
        JPanel contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(new BorderLayout(0, 0));

        canvasPanel = new CanvasPanel();
        canvasPanel.setPreferredSize(new Dimension(600, 500));

        canvasPanel.setForeground(Color.WHITE);
        canvasPanel.setBackground(Color.BLACK);
        canvasPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));

        JScrollPane scrollPane = new JScrollPane(canvasPanel);
        contentPane.add(scrollPane, BorderLayout.CENTER);

        JToolBar toolBar = new JToolBar();
        toolBar.setFloatable(false);
        contentPane.add(toolBar, BorderLayout.SOUTH);

        tipLabel = new JLabel("");
        toolBar.add(tipLabel);

        JPanel controlPanel = new JPanel();
        contentPane.add(controlPanel, BorderLayout.EAST);

        BoxLayout boxLayout = new BoxLayout(controlPanel, BoxLayout.Y_AXIS);
        controlPanel.setLayout(boxLayout);

        JPanel optionalPanel = new JPanel();
        optionalPanel.setBorder(new TitledBorder(null, "选项", TitledBorder.LEADING, TitledBorder.TOP, null, null));
        GridBagLayout gblOptionalLayout = new GridBagLayout();
        gblOptionalLayout.columnWidths = new int[]{120};
        gblOptionalLayout.rowHeights = new int[]{30, 30, 30, 30};
        optionalPanel.setLayout(gblOptionalLayout);
        controlPanel.add(optionalPanel);

        showWeightCheckbox = new JCheckBox("显示权重");
        optionalPanel.add(showWeightCheckbox, createGridBagConstraints(0));

        directedGraphCheckbox = new JCheckBox("有向图");
        optionalPanel.add(directedGraphCheckbox, createGridBagConstraints(1));

        keepLoopRunningCheckbox = new JCheckBox("循环运行");
        optionalPanel.add(keepLoopRunningCheckbox, createGridBagConstraints(2));

        JPanel operatePanel = new JPanel();
        operatePanel.setBorder(new TitledBorder(null, "操作", TitledBorder.LEADING, TitledBorder.TOP, null, null));
        GridBagLayout gridOperateLayout = new GridBagLayout();
        gridOperateLayout.columnWidths = new int[]{120};
        gridOperateLayout.rowHeights = new int[]{40, 40, 40, 40};
        operatePanel.setLayout(gridOperateLayout);
        controlPanel.add(operatePanel);

        runOrStopButton = new JButton("运行");
        operatePanel.add(runOrStopButton, createGridBagConstraints(0));
        runOrStopButton.setForeground(Color.BLUE);

        exchangeButton = new JButton("调换");
        exchangeButton.setToolTipText("交换起点和终点");
        operatePanel.add(exchangeButton, createGridBagConstraints(1));

        cleanLinesButton = new JButton("清空连线");
        operatePanel.add(cleanLinesButton, createGridBagConstraints(2));

        cleanAllButton = new JButton("清空所有");
        operatePanel.add(cleanAllButton, createGridBagConstraints(3));
        cleanAllButton.setForeground(Color.RED);

        JPanel messagePanel = new JPanel();
        messagePanel.setBorder(new TitledBorder(null, "提示", TitledBorder.LEADING, TitledBorder.TOP, null, null));
        GridBagLayout gridMessageLayout = new GridBagLayout();
        gridMessageLayout.columnWidths = new int[]{120};
        gridMessageLayout.rowHeights = new int[]{40, 40, 40, 40};
        messagePanel.setLayout(gridMessageLayout);
        controlPanel.add(messagePanel);

        messagePanel.add(new JLabel("1. 鼠标左键，添加节点"), createGridBagConstraints(0));
        messagePanel.add(new JLabel("2. Ctrl+左键，移动节点"), createGridBagConstraints(1));
        messagePanel.add(new JLabel("3. Shift+左键，移除节点"), createGridBagConstraints(2));
    }

    private void createMenu() {
        JMenuBar menuBar = new JMenuBar();
        setJMenuBar(menuBar);

        JMenu fileMenu = new JMenu("文件");
        menuBar.add(fileMenu);

        JMenuItem importMenuItem = new JMenuItem("导入");
        importMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.CTRL_MASK));
        importMenuItem.addActionListener(e -> doImport());
        fileMenu.add(importMenuItem);

        JMenuItem exportMenuItem = new JMenuItem("导出");
        exportMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK));
        exportMenuItem.addActionListener(e -> doExport());
        fileMenu.add(exportMenuItem);

        JMenuItem rawImageItem = new JMenuItem("设置底图");
        rawImageItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, InputEvent.CTRL_MASK));
        rawImageItem.addActionListener(e -> doSetBackgroundImage());
        fileMenu.add(rawImageItem);

        JMenuItem exitMenuItem = new JMenuItem("退出");
        exitMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.ALT_MASK));
        exitMenuItem.addActionListener(e -> MainFrame.this.dispose());
        fileMenu.add(exitMenuItem);

        JMenu editMenu = new JMenu("编辑");
        menuBar.add(editMenu);

        JMenuItem previousMenuItem = new JMenuItem("上一步");
        previousMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK));
        previousMenuItem.addActionListener(e -> canvasPanel.historyToPrevious());
        editMenu.add(previousMenuItem);

        JMenuItem nextMenuItem = new JMenuItem("下一步");
        nextMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_MASK));
        nextMenuItem.addActionListener(e -> canvasPanel.historyToNext());
        editMenu.add(nextMenuItem);

        JMenu helpMenu = new JMenu("帮助");
        menuBar.add(helpMenu);

        JMenuItem informationMenuItem = new JMenuItem("信息");
        informationMenuItem.addActionListener(e -> new HelpDialog().setVisible(true));
        helpMenu.add(informationMenuItem);

        JMenuItem aboutMenuItem = new JMenuItem("关于");
        aboutMenuItem.addActionListener(e -> JOptionPane.showMessageDialog(MainFrame.this, ABOUT_DESCRIPTION));
        helpMenu.add(aboutMenuItem);


    }

    private void init() {
        directedGraphCheckbox.setSelected(true);
        canvasPanel.setDirectedGraph(true);
        keepLoopRunningCheckbox.setSelected(true);
        canvasPanel.setKeepLoopRunning(true);
    }

    /**
     * 处理拖拽事件
     */
    private void onDrop(DropTargetDropEvent dropEvent) {
        try {
            if (!dropEvent.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
                dropEvent.rejectDrop();
                return;
            }

            dropEvent.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
            List<?> list = (List<?>) (dropEvent.getTransferable().getTransferData(DataFlavor.javaFileListFlavor));
            for (Object aList : list) {
                File f = (File) aList;
                String path = f.getAbsolutePath().toLowerCase();
                if (path.endsWith(".json")) {
                    NodeData nodeData = ImportUtil.importFrom(path);
                    canvasPanel.loadNodes(nodeData);
                    tipLabel.setText(String.format("导入成功！节点数=%d", nodeData.getNodeCount()));
                }
            }
            dropEvent.dropComplete(true);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    private void doExport() {
        if (canvasPanel.getNodeCount() == 0) {
            tipLabel.setText("没有任何节点，不能导出");
            return;
        }
        JFileChooser chooser = new JFileChooser();
        chooser.setDialogTitle("导出...");
        chooser.setDialogType(JFileChooser.SAVE_DIALOG);
        chooser.setSelectedFile(new File("nodes"));
        chooser.setFileFilter(new FileNameExtensionFilter("*.json", "json"));

        if (chooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
            File selected = chooser.getSelectedFile();
            if (selected == null) {
                return;
            }

            String path = selected.getPath();
            if (!selected.getPath().contains(".")) {
                FileNameExtensionFilter currentFilter = (FileNameExtensionFilter) chooser.getFileFilter();
                path += "." + currentFilter.getExtensions()[0];
            }
            ExportUtil.exportTo(canvasPanel.buildNodeData(), path);
            tipLabel.setText(String.format("导出成功！节点数=%d", canvasPanel.getNodeCount()));
        }
    }

    private void doImport() {
        JFileChooser chooser = new JFileChooser();
        chooser.setDialogTitle("导入数据...");
        chooser.setDialogType(JFileChooser.OPEN_DIALOG);
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        chooser.setFileFilter(new FileNameExtensionFilter("*.json", "json"));

        if (chooser.showOpenDialog(MainFrame.this) == JFileChooser.APPROVE_OPTION) {
            File selected = chooser.getSelectedFile();
            if (selected != null) {
                String path = selected.getPath();
                NodeData nodeData = ImportUtil.importFrom(path);
                canvasPanel.loadNodes(nodeData);
                tipLabel.setText(String.format("导入成功！节点数=%d", nodeData.getNodeCount()));
            }
        }
    }

    private void doSetBackgroundImage() {
        JFileChooser chooser = new JFileChooser();
        chooser.setDialogTitle("选择底图...");
        chooser.setDialogType(JFileChooser.OPEN_DIALOG);
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        chooser.setFileFilter(new FileNameExtensionFilter("*.jpg|*.png", "jpg", "png"));

        if (chooser.showOpenDialog(MainFrame.this) == JFileChooser.APPROVE_OPTION) {
            File selected = chooser.getSelectedFile();
            if (selected != null && selected.exists()) {
                try {
                    canvasPanel.setBackgroundImage(ImageIO.read(selected));
                } catch (IOException e) {
                    log.error("设置底图失败", e);
                }
            }
        }
    }

    private void runOrStop() {
        boolean isRunnable = canvasPanel.isRunnable();
        updateControl(isRunnable);
        if (!isRunnable) {
            // 停止运行
            canvasPanel.stopAction();
            return;
        }
        // 尝试启动，如果启动未成功，则停止
        if (!canvasPanel.startAction()) {
            canvasPanel.stopAction();
            updateControl(false);
        }
        // 点击过快，会导致重复创建线程
    }

    private void updateControl(boolean isReadyToRun) {
        boolean enabled = !isReadyToRun;
        showWeightCheckbox.setEnabled(enabled);
        keepLoopRunningCheckbox.setEnabled(enabled);
        directedGraphCheckbox.setEnabled(enabled);
        cleanLinesButton.setEnabled(enabled);
        cleanAllButton.setEnabled(enabled);
        exchangeButton.setEnabled(enabled);

        runOrStopButton.setText(isReadyToRun ? "停止" : "运行");
    }

    private GridBagConstraints createGridBagConstraints(int gridY) {
        GridBagConstraints gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.fill = GridBagConstraints.BOTH;
        gridBagConstraints.insets = new Insets(0, 0, 5, 0);
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = gridY;
        return gridBagConstraints;
    }
}
